package com.zx._01_基础._013_io;

import com.zx.common.Constant;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.FileStore;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.DosFileAttributeView;
import java.nio.file.attribute.FileOwnerAttributeView;
import java.nio.file.attribute.UserDefinedFileAttributeView;
import java.nio.file.attribute.UserPrincipal;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;

public class Demo_06_NIO_File extends TestCase {

    /**
     * Paths工具类
     *
     * @throws IOException
     */
    public void test1() throws IOException {
        //以当前路径来创建Path对象
        Path path = Paths.get(".");
        //如D:\workspace-4.4\20160228_IO，他的路径数量就是3，D:是1，workspace-4.4是2，20160228_IO是3
        System.out.println("path里面包含的路径数量:" + path.getNameCount());
        System.out.println("path的根路径:" + path.getRoot());
        System.out.println("path的绝对路径:" + path.toAbsolutePath());
        System.out.println("path的绝对路径的根路径:" + path.toAbsolutePath().getRoot());
        System.out.println("path的绝对路径的包含的路径的数量:" + path.toAbsolutePath().getNameCount());
        //多个String来创建path对象
        Path path2 = Paths.get("d:", "备份", "数据库备份", "个人日常管理系统数据库备份");
        System.out.println(path2);
    }

    /**
     * Files 工具类
     *
     * @throws IOException
     */
    public void test2() throws IOException {
        //1.复制文件，返回复制文件的大小，单位是字节
        long copy = Files.copy(Paths.get(Constant.io_read_text), new FileOutputStream(Constant.io_write_text));
        //2.判断文件是否是隐藏的
        System.out.println("判断文件是否是隐藏的" + Files.isHidden(Paths.get(Constant.io_read_text)));
        //3.读取文件中的所有的行
        List<String> readAllLines = Files.readAllLines(Paths.get(Constant.io_read_text), Charset.forName("GBK"));
        for (int i = 0; i < readAllLines.size(); i++) {
            System.out.println("第" + i + "行" + readAllLines.get(i));
        }
        //4.判断文件的大小，单位是字节
        long size = Files.size(Paths.get(Constant.io_read_text));
        System.out.println(size);

        List<String> ss = new ArrayList<>(2);
        ss.add("测试1");
        ss.add("测试2");
        Path write = Files.write(Paths.get(Constant.io_write_text), ss, Charset.forName("utf-8"));

        // 读取磁盘
        FileStore store = Files.getFileStore(Paths.get("c:"));
        System.out.println("C:共有空间" + (store.getTotalSpace() / 1024 / 1024 / 1024) + "G");
        System.out.println("C:可用空间" + (store.getUsableSpace() / 1024 / 1024 / 1024) + "G");
    }

    /**
     * 监控文件夹-被访问事件
     * @throws IOException
     */
    public void test3() throws IOException {
        //FileVisitResult是一个枚举    CONTINUE,
        //TERMINATE 代表可以继续访问
        //TERMINATE	代表终止访问
        //SKIP_SUBTREE	代表可以继续访问，但不访问该文件或目录的子目录树
        //SKIP_SIBLINGS	代表终止访问，但不访问该文件或目录的子目录树
        Files.walkFileTree(Paths.get("D://demo"), new FileVisitor<Path>() {
            /**
             * 访问子目录之前触发
             */
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                return FileVisitResult.CONTINUE;
            }

            /**
             * 访问file时触发
             */
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                System.out.println("****正在访问" + file);
                if (file.endsWith("ftp案例.txt")) {
                    System.out.println("√√√√找到文本文件" + file);
                }
                return FileVisitResult.CONTINUE;
            }

            /**
             * 访问file失败时触发
             */
            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                return FileVisitResult.CONTINUE;
            }

            /**
             * 访问子目录之后触发
             */
            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                System.out.println("目录：" + dir);
                return FileVisitResult.CONTINUE;
            }
        });
    }

    /**
     * 监听文件夹变更事件
     */
    public void test4() throws IOException, InterruptedException {
        //获得文件系统的WatchService对象
        WatchService watchService = FileSystems.getDefault().newWatchService();
        //为D盘注册监听
        Paths.get("d:/").register(watchService,
                StandardWatchEventKinds.ENTRY_CREATE,
                StandardWatchEventKinds.ENTRY_DELETE,
                StandardWatchEventKinds.ENTRY_MODIFY);

        //这里使用了一个死循环，如果web项目则而已在一个监听器中使用
        while (true) {
            //获取下一个文件变化事件
            WatchKey key = watchService.take();
            for (WatchEvent<?> event : key.pollEvents()) {
                System.out.println(event.context() + "文件发生了" + event.kind() + "事件");
            }
            //重设watchkey
            boolean valid = key.reset();
            //如果重设失败，则退出监听
            if (!valid) {
                break;
            }
        }
    }

    /**
     * NIO 文件基本操作
     */
    public void test5() throws IOException {
        //获得想要操作的文件
        Path testPath = Paths.get("src/com/demo/Demo_06_NIO.java");
        //获得基本属性的BasicFileAttributeView，类似于文件属性的视图
        BasicFileAttributeView basicView = Files.getFileAttributeView(testPath, BasicFileAttributeView.class);

        /************************** 获得文件的基本属性 */
        BasicFileAttributes basicViews = basicView.readAttributes();

        System.out.println("创建时间" + basicViews.creationTime());

        System.out.println("最后访问时间" + basicViews.lastAccessTime());

        System.out.println("最后修改时间" + basicViews.lastModifiedTime());

        System.out.println("文件大小" + basicViews.size() + "B");

        /************************** 获得文件属主信息的FileOwnerAttributeView */
        FileOwnerAttributeView ownerView = Files.getFileAttributeView(testPath, FileOwnerAttributeView.class);

        System.out.println("文件所属的用户" + ownerView.getOwner());
        //获得系统中guest对应的用户
        UserPrincipal user = FileSystems.getDefault().getUserPrincipalLookupService().lookupPrincipalByName("guest");
        //修改用户
        //ownerView.setOwner(user);

        /************************** 获得自定义属性的UserDefinedFileAttributeView */
        UserDefinedFileAttributeView userView = Files.getFileAttributeView(testPath,
                UserDefinedFileAttributeView.class);
        //获得所有的属性
        List<String> list = userView.list();
        for (String name : list) {
            ByteBuffer buff = ByteBuffer.allocate(userView.size(name));
            userView.read(name, buff);
            buff.flip();
            String value = Charset.defaultCharset().decode(buff).toString();
            System.out.println(name + "=" + value);
        }
        //添加一个自定义属性
        userView.write("发行人", Charset.defaultCharset().encode("张雪"));

        /************************** 获得DOS属性的DosFileAttributeView */
        DosFileAttributeView dosView = Files.getFileAttributeView(testPath, DosFileAttributeView.class);
        dosView.setHidden(true);//设置为隐藏
        dosView.setReadOnly(true);//设置为只读

    }

    /**
     * NIO读取文本。每次值读取1024B。
     * 使用的技术：channel、buffer（堆内存/对外内存）
     */
    public void test6() {
        FileChannel channel = null;
        // 用于读取数据
        Charset charset = Charset.defaultCharset();
        try {
            // 1. 通过io流获取 channel
            channel = new FileInputStream("D:\\logs\\1.txt").getChannel();

            // 2. 设置缓存区，分配空间，allocate 从堆中申请内存, allocateDirect 从堆外申请内存
            ByteBuffer buf = ByteBuffer.allocate(1024);

            int read = 0;
            StringBuilder sb = new StringBuilder();
            // 3. 读取数据到Buffer
            while ((read = channel.read(buf)) > 0) {
                // 3.1 转为写出模式。允许读取数据
                buf.flip();
                // 3.2 获得本地缓存区中可以读取数据的范围
                int begin = 0;
                int end = buf.limit();

                // 3.3 读取数据
                // a. 只从缓冲区中读取一个字节
                // buf.get();

                // b. 读取end长度的数据
                //byte[] bytes = new byte[end];
                //buf.get(bytes);

                // c. 读取缓存区的数据，并转为文本
                String str = charset.decode(buf).toString();
                sb.append(str);

                // 3.4 转为写入模式，允许后面可以将数据写入缓冲区
                buf.compact();
            }
            System.out.println(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
